home *** CD-ROM | disk | FTP | other *** search
Wrap
/********************************************************************** * * lister print.c -- Version 3.0 * * Developer Technical Support Apple II Sample Code * * Copyright (c) * Apple Computer, Inc. 1988-1990 * All Rights Reserved. * * Written by Keith Rollin. * * This file contains the code which does the printing stuff. This * code was taken nearly intact from the original lister sample * code by Keith Rollin. * **********************************************************************/ #include <types.h> #include <font.h> #include <intmath.h> #include <memory.h> #include <qdaux.h> #include <resources.h> #include "lister.h" static char headerFooter[130]; static char builtHF[255]; static char time[20]; /* String to hold the time when printing. */ static unsigned int pageNum, lineHeight, lineTop, maxLines, lineBottom; static char PSMoveStr[] = " moveto\r"; static char PSShow1[] = " ("; static char PSShow2[] = ") show\r"; static char NoMemAlertString[] = "32/Not enough memory to print the file!!!/^#0"; /**********************************************************************/ /* This routine handles the print record. Handling it is delayed until ** it is needed. This makes starting up the application a little ** faster. The print driver isn't loaded until the user commits to ** needing it. Therefore, since the driver isn't loaded at startup ** time, startup is a little faster. ** The print.prRec field is initially NULL. If it is, then we don't ** have a print record yet. If we don't, then make one. ** Once it is made, then do the appropriate function. ** Function 0: PrStlDialog ** Function 1: PrJobDialog */ unsigned int prRecHandler(command) unsigned int command; { unsigned int confirmFlag, postScript; PrRec **prRec, *prRecPtr; zapLocals(); if (!(prRec = print.prRec)) { prRec = (PrRecHndl)NewHandle((unsigned long)sizeof(PrRec), _ownerid, 0, 0L); if (_toolErr) return(0); print.prRec = prRec; PrDefault(prRec); prRecPtr = *prRec; postScript = (prRecPtr->prInfo.iDev == 3); /* postScript true if we are printing to a LaserWriter. */ if (postScript) { prRecPtr->prStl.wDev |= 0x02; /* bit 1 on: LaserWriter landscape mode. */ prRecPtr->prStl.crWidth = 2; /* crWidth=2 LaserWriter condensed mode. */ } else { prRecPtr->prStl.wDev &= 0xFFF9; /* bit 1 off: ImageWriter landscape mode. */ } /* bit 2 off: ImageWriter condensed mode. */ PrValidate(prRec); /* Do this due to the changes to the record. */ } switch (command) { case 0: confirmFlag = PrStlDialog(prRec); break; case 1: confirmFlag = PrJobDialog(prRec); break; } return(confirmFlag); } /**********************************************************************/ /* This routine gets the current #-of-columns text and converts it ** into an integer. It then makes sure that it is between 1 & 4. */ unsigned int getNumCols() { char colText[16]; unsigned int col; zapLocals(); fmdLEGetText(mainWindow, Columns, colText); /* Get the #-columns text via a fakeModalDialog access call. */ col = atoi(colText + 1); /* Convert text to integer. */ if (!col) col++; /* Bring the # of columns within range. */ if (col > 4) col = 4; return(col); } /**********************************************************************/ /* Convert a column # to a coordinate for the left margin of that column. */ unsigned int setMargin(col) unsigned int col; { return((print.myPage.h2 - print.myPage.h1) * col / getNumCols() + print.myPage.h1); } /**********************************************************************/ /* This routine take the string that is stored in the headerFooter string ** buffers, and starts parsing it, starting at the character indexed by the ** variable 'src'. It will read up until the ned of the string or the next ** '|', parsing embedded commands, and placing the resulting string into ** 'builtHF'. ** ** Special substitutions ('@' marks embedded command): ** D: replace with current date. ** T: replace with current time. ** L: replace with full path name. ** S: replace with file name (short pathname). ** P: replace with page number. */ unsigned int getLineSegment(src) unsigned int src; { unsigned int dest, i, max, sLen; char pageText[3], *cptr; zapLocals(); dest = 0; sLen = headerFooter[0]; while ((headerFooter[src] != '|') && (src <= sLen)) { if (headerFooter[src] == '@') { src++; /* Skip over the '@' */ cptr = (*file.pathRef + 2); max = *(unsigned int *)(*file.pathRef + 2); if (max > 127) max = 127; cptr += 2; switch (headerFooter[src++]) { /* skips over format code */ case 'd': case 'D': for (i = 0;i <= 7; builtHF[dest++] = (time[i++] & 0x7F)); break; case 't': case 'T': max = ReadBParam(0x35) ? 16 : 19; for (i = 9; i <= max; builtHF[dest++] = (time[i++] & 0x7F)); break; case 'l': case 'L': for (i = 0; i < max; builtHF[dest++] = cptr[i++]); break; case 's': case 'S': for (i = 0; i < max; builtHF[dest++] = cptr[i++]); break; case 'p': case 'P': Int2Dec(pageNum, pageText, 3, false); for (i=0; i<=2; builtHF[dest++] = pageText[i++]); break; default: src -= 2; /* Go back to the '@' and copy it */ builtHF[dest++] = headerFooter[src++]; /* end up pointing to ex-format code, and printing it */ break; } /* end switch */ } else { builtHF[dest++] = headerFooter[src++]; } /* == '@' */ } /* == '|' */ builtHF[dest] = '\0'; /* replace the '|' with a NULL, ending the string */ return(++src); /* skip over the '|' */ } /**********************************************************************/ /* If we are printing in PostScript mode, we need to fix up the string. ** The characters "/", "(", and ")" are special to PostScript. In order ** to print them, we need to stick a "\" in front of them. */ void fixString(string) char string[]; { char *srcPtr, *destPtr; char ch; static char strCopy[255]; zapLocals(); srcPtr = string; /* Set up pointers to the source string and */ destPtr = strCopy; /* to the buffer where a copy of it will go */ while (*destPtr++ = *srcPtr++); /* Make a copy of the string */ srcPtr = strCopy; /* reset our pointers */ destPtr = string; while (ch = *srcPtr++) { /* Now fix all of the characters */ if ((ch == '\\') || (ch == '(') || (ch == ')')) { *destPtr++ = '\\'; } if (ch != 0x0D) *destPtr++ = ch; /* and remove carriage returns! */ } *destPtr = '\0'; /* make this thing a C String */ } /**********************************************************************/ /* Sends the specified string out at the specified location. This routine ** determines if we are in PostScript mode, and will create a PostScript ** command string if so. If not, it will simply use QuickDraw to draw the ** string. */ void sendString(line, x, y) char line[]; unsigned int x, y; { char xStr[6], yStr[6]; static char printStr[255]; zapLocals(); if (!line[0]) return; /* Do nothing on a NULL string. */ if (print.postScript) { fixString(line); /* Escape out all '\', '(', and ')' */ Int2Dec(x, xStr, 5, true); /* Put XY coordinates into ASCII */ Int2Dec(y, yStr, 5, true); xStr[5] = '\0'; /* Int2Dec just returns the ASCII chars */ yStr[5] = '\0'; /* We need to turn them into C Strings */ strcpy(printStr, xStr); /* Build a moveTo PostScript command */ strcat(printStr, yStr); strcat(printStr, PSMoveStr); strcat(printStr, PSShow1); strcat(printStr, line); /* Catenate the string to print. */ strcat(printStr, PSShow2); /* Catenate a 'show' command. */ DrawCString(printStr); /* Send it to LaserWriter driver. */ } else { MoveTo(x, y); /* Use QuickDraw to position and */ DrawCString(line); /* display the string. */ } } /**********************************************************************/ /* Takes a string assumed to be a header or footer, and prints it where ** it is told to. This routine gets the current time, and then calls ** getLineSegment() to get part of the header/footer and parse out the ** special codes in it. Then it calls sendString to print it out. It ** this this 3 times for the 3 possible segments of the header/footer. */ void drawHeaderFooter(v, left, right) unsigned int v, left, right; { unsigned int src, width; zapLocals(); ReadAsciiTime(time); /* Get the current time for parsing */ src = 1; /* Start parsing the string at the start */ src = getLineSegment(src); /* Get a parsed line segment. Set src. */ sendString(builtHF, left + 10, v); /* Print it in the left slot */ src = getLineSegment(src); /* Get another segment where we left off */ width = CStringWidth(builtHF); /* Find width in order to center it */ sendString(builtHF, (right + left - width) / 2, v); /* Print it centered */ getLineSegment(src); /* Get the last segment. */ width = CStringWidth(builtHF); /* Get width to right justify it */ sendString(builtHF, right - 10 - width, v); /* and print it. */ } /**********************************************************************/ /* Read the header string from the lineEdit control, and call upon ** drawHeaderFooter() to format it, position it, and print it. */ void printHeader(column) unsigned int column; { unsigned int row; zapLocals(); fmdLEGetText(mainWindow, Header, headerFooter); row = print.myPage.v1 + lineHeight + lineHeight / 2; drawHeaderFooter(row, setMargin(column), setMargin(column + 1)); } /**********************************************************************/ /* Read the Footer string from the EditLine item, and call upon ** DrawHeaderFooter() to format it, position it, and print it. */ void printFooter(column) unsigned int column; { unsigned int row; zapLocals(); fmdLEGetText(mainWindow, Footer, headerFooter); row = print.myPage.v2 - lineHeight + lineHeight / 2; drawHeaderFooter(row, setMargin(column), setMargin(column + 1)); } /**********************************************************************/ /* Draw any borders needed. If the Global "Borders Needed" flag ** (print.myBorders[0].used) is FALSE, then do nothing. Else, check the ** flags for each part of the borders array, and print the border line if ** appropriate. */ void drawPageBorders() { unsigned int v1, v2, h1, h2, margin, column; zapLocals(); v1 = print.myPage.v1; h1 = print.myPage.h1; v2 = print.myPage.v2; h2 = print.myPage.h2; if (print.myBorders[0].used) { if (print.myBorders[1].used) { MoveTo(h1, v1); LineTo(h2, v1); } if (print.myBorders[2].used) { MoveTo(h1, v1 + lineHeight * 2); LineTo(h2, v1 + lineHeight * 2); } if (print.myBorders[3].used) { MoveTo(h1, v2 - lineHeight * 2); LineTo(h2, v2 - lineHeight * 2); } if (print.myBorders[4].used) { MoveTo(h1, v2); LineTo(h2, v2); } if (print.myBorders[5].used) { MoveTo(h1, v1); LineTo(h1, v2); } if (print.myBorders[7].used) { MoveTo(h2, v1); LineTo(h2, v2); } if ((print.myBorders[6].used) && (getNumCols() > 1)) { for (column = 1; column < getNumCols(); column++) { margin = setMargin(column); MoveTo(margin, v1); LineTo(margin, v2); } /* end for */ } /* end if print.myBorders[6] etc. */ } /* end of "if any borders are used at all" */ } /**********************************************************************/ /* This function makes any necessary adjustments to the text, due to ** the format the text is in. (Merlin has hi-bit on chars, for ** example.) */ void adjustText(line) char *line; { unsigned int orval, andval, c; zapLocals(); switch(fmdWhichRadio(mainWindow, 1)) { case 0: orval = 0x00; andval = 0xFF; break; case 1: orval = 0x00; andval = 0x7F; break; case 2: orval = 0x80; andval = 0xFF; break; } for (;; line++) { if (!(c = *line)) break; *line = (c | orval) & andval; } } /**********************************************************************/ unsigned int nextTabLoc(loc) unsigned int loc; { char *cptr; unsigned int c, dt, oldt, t, temp; zapLocals(); for (dt = oldt = t = 0, cptr = print.tabsData + 1;;) { temp = atoi(cptr) * CharWidth('0'); /* Representative character */ /* width for tabbing purposes. */ if (temp > t) { /* Make sure that we aren't going backwards. */ oldt = t; t = temp; /* t is now bigger than oldt. */ } if (loc < t) return(t); /* That was easy. */ for (;; cptr++) if (((c = *cptr) == ',') || (c == 0xC9) || (!c)) break; /* Move to next tab value. */ if (!c) return(loc); /* No more tabs. Return location passed to us. */ if (!*++cptr) break; /* String ended with a comma or ellipses. */ } if (!(c = t - oldt)) return(loc); /* Ignore the tab-'til op, since it is invalid. */ for (;;) { /* Do the tab-'til thing, since it is valid. */ t += c; if (loc < t) return(t); } } /**********************************************************************/ /* Print a single column. It prints any headers and/or footers, and then ** loops through the number of lines printable on the page. For every time ** through the loop, it reads a line from the file, and calls sendString ** to print it in thr right location and right mode (PostScript or Quick- ** Draw II). If we reached the end of the file, return this fact to ** printAPage(). */ unsigned int printAColumn(column) unsigned int column; { WindowPtr fwptr; unsigned int orval, andval, lineNum, margin, x, y, i, c, done; char *cptr; zapLocals(); switch(fmdWhichRadio(mainWindow, 1)) { case 0: orval = 0x00; andval = 0xFF; break; case 1: orval = 0x00; andval = 0x7F; break; case 2: orval = 0x80; andval = 0xFF; break; } if (print.haveHeader) printHeader(column); if (print.haveFooter) printFooter(column); lineNum = 1; margin = setMargin(column); do { done = (!readFile(&file)); if (_fileErr) { done = 1; /* If we have a file error, we are done. */ if (_fileErr != eofEncountered) /* If the file error was not EOF, then */ fileErr(); /* we have to report it. */ } if (!done) { if (print.tabChr == 160) { /* Danger: Merlin file. */ if (line[0] == ';' + 128) { /* DANGER: Leading semi. */ BlockMove(line, line + 3, (unsigned long)252); line[255] = 0; line[0] = line[1] = line[2] = 160; } } /* The leading semi now is at 3rd tab location, like Merlin says. */ x = 0; y = lineNum * lineHeight + lineTop; cptr = line; for (i = 0;;) { c = cptr[i]; /* See what we have. */ if ((c == print.tabChr) || (!c)) { /* Are we at a tab or EOL? */ cptr[i] = 0; /* Terminate this string segment. */ sendString(cptr, x + margin + 10, y); /* Print the segment. */ if (!c) break; /* It was our last segment. */ x += CStringWidth(cptr); /* Tab over for next line segment. */ x = nextTabLoc(x); cptr += i + 1; /* Prepare for the next segment. */ i = 0; } else cptr[i++] = (c | orval) & andval; } } } while ((!done) && (lineNum++ < maxLines)); return(done); } /**********************************************************************/ /* Called by the heart of the print loop to print a single page. This ** routine draws the borders, sets up the printer for PostScript input ** if it is a LaserWriter, prints number of columns, and then turns ** off PostScript mode if appropriate. This routine also detects if ** we reached the end of the file or not, and sends that bit of information ** back to the print loop to tell it to stop when it's time. */ unsigned int printAPage() { unsigned int column, prErrorSave; unsigned int done; zapLocals(); column = 0; MoveTo(20,20); /* Force a font to be set. */ DrawChar(' '); drawPageBorders(); if (print.postScript) { PicComment(PostScriptBegin, 0, NIL); /* Turn on PostScripting */ PicComment(TextIsPostScript, 0, NIL); /* Set form of PostScripting */ } do { /* Print x number of columns */ done = printAColumn(column); column++; pageNum++; } while ((!done) && (column < getNumCols())); if (print.postScript) { /* If an error occurred, PrError() would cause all Print Manager */ /* routines to short-circuit themselves. If so, then we won't be */ /* able to turn off PostScripting! So we have to save the current */ /* value of PrError(), set it to zero, turn off PostScripting, and */ /* then set PrError() back to its old value. NOTE: This is only a */ /* problem with System Disk 3.2; the Print Manager on System Disk */ /* 4.0 clears PostScript mode on PrClosePage(). */ prErrorSave = PrError(); PrSetError(0); PicComment(PostScriptEnd,0,NIL); /* Turn off PostScripting. */ PrSetError(prErrorSave); } return(done); /* Tell Print Loop if this was the Last Page or not. */ } /**********************************************************************/ void showError(err) unsigned int err; { char *errStr; static char PrintErrNum[] = "xxxx"; static char *SubStrings[] = {PrintErrNum}; zapLocals(); if (err) { switch(err) { case prAbort: errStr = "32/Printing was canceled because you pressed \021-./^#0"; break; case missingDriver: errStr = "32/Specified driver not in the DRIVERS subdirectory of the SYSTEM subdirectory/^#0"; break; case portNotOn: errStr = "32/Specified port not selected in the Control Panel./^#0"; break; case noPrintRecord: errStr = "32/No print record was specified./^#0"; break; case badLaserPrep: errStr = "42/The version of the LaserPrep file in the LaserWriter is not compatible with this version of the Print Manager/^#0"; break; case badLPFile: errStr = "42/The version of the LaserPrep file in the DRIVERS subdirectory in the SYSTEM subdirectory is not compatible with this verson of the Print Manager/^#0"; break; case papConnNotOpen: errStr = "32/Connection couldn't be established with the LaserWriter./^#0"; break; case papReadWriteErr: errStr = "32/Read-Write error on the LaserWriter./^#0"; break; case 0x1308: errStr = "32/Connection to the printer failed./^#0"; break; default: errStr = NULL; } if (errStr) AlertWindow(0, NULL, errStr); else { Int2Hex(PrError(), PrintErrNum, 4); AlertWindow(0, SubStrings, "32/Print error $*0 occured!!!/^#0"); } } } /**********************************************************************/ /* Routine called in response to choosing Print from the menu. It sets ** up some variables, and then goes into the Classic Print Loop: ** ** Display Job Dialog box ** Open the Document ** Open the Page ** Install my font ** Print the Page ** Close the Page ** Until all pages are printed ** UnSpool the picture if Printing to an ImageWriter */ void doPrint() { WindowPtr cancelPrintWindow; GrafPortPtr keepPort, prPort; unsigned long id; PrRecPtr prRecPtr; FontInfoRecord theInfoRec; PrStatusRec myStatusRec; FontHndl oldFont; unsigned int done, val, i, firstPage, lastPage; /* Get some last minute information, and give user a chance to Cancel */ zapLocals(); print.myBorders[0].used = GetCtlValue(GetCtlHandleFromID(mainWindow, UseBorder)); val = GetCtlValue(GetCtlHandleFromID(mainWindow, BoxProc)); for (i = 1; i <= 7; i++) { print.myBorders[i].used = (val & 0x01); val = val >> 1; } if (prRecHandler(1)) { getTabsInfo(); /* The tabsWindow may be active, in which case, ** we don't have the latest info. */ keepPort = GetPort(); cancelPrintWindow = NewWindow2(NULL, NULL, NULL, NULL, 2, NowPrintingID, rWindParam1); if (_toolErr) cancelPrintWindow = NULL; else { BeginUpdate(cancelPrintWindow); /* The Begin & End update get rid */ DrawControls(cancelPrintWindow); /* of the update event. This is */ EndUpdate(cancelPrintWindow); /* good. */ } prRecPtr = *print.prRec; /* Dereference the print record. */ /* We need information. */ firstPage = prRecPtr->prJob.iFstPage; /* Process # pages to print. */ lastPage = prRecPtr->prJob.iLstPage; if (!firstPage) firstPage = 1; if (lastPage < firstPage) lastPage = firstPage; firstPage = (--firstPage) / getNumCols() + 1; lastPage = (--lastPage) / getNumCols() + 1; prRecPtr->prJob.iFstPage = firstPage; prRecPtr->prJob.iLstPage = lastPage; print.postScript = (prRecPtr->prInfo.iDev == 3); /* print.postScript true if we are printing to a LaserWriter. */ print.sideways = prRecPtr->prStl.wDev & 0x02; if (!print.postScript) print.sideways ^= 0x02; /* Set up our own logical page size. This lets me set some margin */ /* space for 3-hole punches. */ print.myPage.v1 = prRecPtr->prInfo.rPage.v1 + (print.sideways ? 50:5); print.myPage.h1 = prRecPtr->prInfo.rPage.h1 + (print.sideways ? 5:50); print.myPage.v2 = prRecPtr->prInfo.rPage.v2 - 5; print.myPage.h2 = prRecPtr->prInfo.rPage.h2 - 5; /* print.myPage will be used for page calculations. */ oldFont = GetFont(); InstallFont(print.theFont.fidLong, 0); /* Do this once so we can get info on it. */ GetFontInfo(&theInfoRec); SetFont(oldFont); /* Set us back to the System Font. */ lineHeight = theInfoRec.ascent + theInfoRec.descent + theInfoRec.leading; pageNum = 1; /* Initialize the page number counters. */ /* Set up variables used to determine where to start and stop */ /* printing on the page. */ /* */ /* Step One: assume that there are no borders, headers, or */ /* footers. */ lineTop = print.myPage.v1; lineBottom = print.myPage.v2; /* Step Two: See what types of borders we are using, and */ /* adjust the top and bottom margins accordingly. */ if (print.myBorders[0].used) { if (print.myBorders[1].used) lineTop = print.myPage.v1 + lineHeight; if (print.myBorders[2].used) lineTop = print.myPage.v1 + lineHeight * 3; if (print.myBorders[4].used) lineBottom = print.myPage.v2 - lineHeight; if (print.myBorders[3].used) lineBottom = print.myPage.v2 - lineHeight * 3; } /* Step Three: Get the header and footer text. If there is any, */ /* then set aside space for them. */ fmdLEGetText(mainWindow, Header, headerFooter); print.haveHeader = *headerFooter; fmdLEGetText(mainWindow, Footer, headerFooter); print.haveFooter = *headerFooter; if (print.haveHeader) lineTop = print.myPage.v1 + lineHeight * 3; if (print.haveFooter) lineBottom = print.myPage.v2 - lineHeight * 3; /* Figure out how many lines that comes out to. */ maxLines = (lineBottom - lineTop) / lineHeight; /* Attempt to open the file. If that succeeds, then go into the */ /* printing loop. */ if (!openFile(&file)) { /* If file opened without incident... */ /* We now enter into the Classical Print Loop. Notice the */ /* gyrations I go through in the middle with the checking of */ /* PrError(). Normally, you wouldn't think that this would be */ /* necessary, as the Print Manager will automatically skip over */ /* the rest of the printing if an error occurs. However, I have */ /* to make my routine more intelligent for 2 reasons: */ /* 1) I can abort the reading of the rest of the file if */ /* I know that an error has occured. Even though the */ /* Print manager won't print anything if an error */ /* occurs, I can speed things up by doing this. */ /* 2) The Print Manager short-circuits all of its routines */ /* when an error occurs. It makes a check of PrError() */ /* at the beginning of all routines and if it is not */ /* equal to zero, it returns before doing anything */ /* else. However, assume the case where the user */ /* presses Cmd-. before PrOpenPage() is called for the */ /* time. Among other things, PrOpenPage() is respon- */ /* sible for setting the port to the Printer Port */ /* ('prPort' as listed below). If Cmd-. is pressed, */ /* PrOpenPage() will not do that, and the port is left */ /* set to my Dialog Window. If I were to then perform */ /* my InstallFont(), I would set the font of the */ /* Dialog Window, and not the Printer Port, like I */ /* wanted. So I have to make a check of PrError to see */ /* if it is OK to proceed. */ prPort = PrOpenDoc(print.prRec, NULL); /* Start a print session */ if (_toolErr) PrSetError(_toolErr); do { PrOpenPage(prPort, NULL); /* Open port for printing into. */ if (_toolErr) PrSetError(_toolErr); if (PrError()) { /* If an error occurs... */ done = TRUE; /* ... get outta here! */ } else { /* If no error occurs... */ InstallFont(print.theFont.fidLong, 0); /* ...reset the font... */ done = printAPage(); /* ...and print a page. */ } PrClosePage(prPort); /* Close the printer's GrafPort */ if (_toolErr) PrSetError(_toolErr); } while (!done); /* "Bop 'til you drop!" */ PrCloseDoc(prPort); /* Close this print session. */ if (_toolErr) PrSetError(_toolErr); closeFile(&file); /* Close the file. */ fileErr(); if (!PrError()) { /* If no error occured... */ if (MaxBlock() < 10240) { /* Enough memory to de-spool? */ PurgeAll(_ownerid); /* No - try to get some. */ CompactMem(); if (MaxBlock() < 10240) /* Enough memory now? */ /* Not enough memory to de-spool to the ImageWriter */ /* so show a dialog box saying so. However, that */ /* begs the question of whether or not we have */ /* enough memory to show this dialog box... */ AlertWindow(0, NULL, NoMemAlertString); SetPort(keepPort); return; }; PrPicFile(print.prRec, NULL, &myStatusRec); } else showError(PrError()); } else fileErr(); /* The file never even opened. */ SetPort(keepPort); CloseWindow(cancelPrintWindow); /* Remove status box. */ cancelPrintWindow = NULL; } }